package com.simple.quartz.service.impl;


import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.simple.common.constant.ScheduleConstants;
import com.simple.common.exception.TaskException;
import com.simple.framework.core.domain.vo.PagingVo;
import com.simple.framework.utils.BeanCopyUtils;
import com.simple.quartz.domain.dto.AddSysJobDto;
import com.simple.quartz.domain.dto.ChangeJobStatusDto;
import com.simple.quartz.domain.dto.ListSysJobDto;
import com.simple.quartz.domain.entity.SysJob;
import com.simple.quartz.mapper.SysJobMapper;
import com.simple.quartz.service.SysJobService;
import com.simple.quartz.utils.CronUtils;
import com.simple.quartz.utils.ScheduleUtils;
import lombok.extern.slf4j.Slf4j;
import org.quartz.JobDataMap;
import org.quartz.JobKey;
import org.quartz.Scheduler;
import org.quartz.SchedulerException;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import java.util.List;
import java.util.Set;

/**
 * @Description 定时任务调度表(SysJob)表服务实现类
 * @Author xiaoke
 * @Date 2024/8/19 19:51
 * @Version 1.0
 */
@Slf4j
@Service
public class SysJobServiceImpl implements SysJobService {
    private final SysJobMapper sysJobMapper;

    private final Scheduler scheduler;

    public SysJobServiceImpl(SysJobMapper sysJobMapper, Scheduler scheduler) {
        this.sysJobMapper = sysJobMapper;
        this.scheduler = scheduler;
    }


    /**
     * 项目启动时，初始化定时器
     */
    @PostConstruct
    public void init() throws SchedulerException, TaskException {
        log.info("定时任务初始化开始");
        scheduler.clear();
        for (SysJob sysJob : listAll()) {
            ScheduleUtils.createScheduleJob(scheduler, sysJob);
        }
        log.info("定时任务初始化启动");
    }


    @Override
    public PagingVo<SysJob> list(ListSysJobDto dto) {
        //分页查询
        Page<SysJob> page = new Page<>(dto.getPageNum(), dto.getPageSize());
        //查询条件
        LambdaQueryWrapper<SysJob> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper
                .eq(!ObjectUtil.isNull(dto.getJobName()), SysJob::getJobName, dto.getJobName())
                .eq(!ObjectUtil.isNull(dto.getJobGroup()), SysJob::getJobGroup, dto.getJobGroup())
                .eq(!ObjectUtil.isNull(dto.getStatus()), SysJob::getStatus, dto.getStatus())
                .eq(!ObjectUtil.isNull(dto.getCronExpression()), SysJob::getCronExpression, dto.getCronExpression());

        sysJobMapper.selectPage(page, queryWrapper);
        return new PagingVo<>(page.getRecords(), page.getTotal());

    }

    @Transactional
    @Override
    public boolean pause(SysJob job) throws SchedulerException {
        Long jobId = job.getId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        int rows = sysJobMapper.updateById(job);
        if (rows > 0) {
            scheduler.pauseJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows > 0;
    }

    @Transactional
    @Override
    public boolean resume(SysJob job) throws SchedulerException {
        Long jobId = job.getId();
        String jobGroup = job.getJobGroup();
        job.setStatus(ScheduleConstants.Status.NORMAL.getValue());
        int rows = sysJobMapper.updateById(job);
        if (rows > 0) {
            scheduler.resumeJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows > 0;
    }

    @Transactional
    @Override
    public boolean delete(SysJob job) throws SchedulerException {
        Long jobId = job.getId();
        String jobGroup = job.getJobGroup();
        int rows = sysJobMapper.deleteById(jobId);
        if (rows > 0) {
            scheduler.deleteJob(ScheduleUtils.getJobKey(jobId, jobGroup));
        }
        return rows > 0;
    }

    @Transactional
    @Override
    public void deleteJobByIds(Set<Long> ids) throws SchedulerException {
        for (Long id : ids) {
            delete(sysJobMapper.selectById(id));
        }
    }

    @Transactional
    @Override
    public Boolean changeStatus(Long id, ChangeJobStatusDto status) throws SchedulerException {
        boolean rows = false;
        SysJob job = sysJobMapper.selectById(id);
        if (ScheduleConstants.Status.NORMAL.getValue().equals(status.getStatus())) {
            rows = resume(job);
        } else if (ScheduleConstants.Status.PAUSE.getValue().equals(status.getStatus())) {
            rows = pause(job);
        }
        return rows;
    }

    @Transactional
    @Override
    public boolean run(Long id) throws SchedulerException {
        SysJob job = sysJobMapper.selectById(id);
        if (job == null) return false;
        boolean result = false;
        Long jobId = job.getId();
        String jobGroup = job.getJobGroup();
        SysJob properties = sysJobMapper.selectById(jobId);
        // 参数
        JobDataMap dataMap = new JobDataMap();
        dataMap.put(ScheduleConstants.TASK_PROPERTIES, properties);
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            result = true;
            scheduler.triggerJob(jobKey, dataMap);
        }
        return result;
    }

    @Transactional
    @Override
    public Long insert(AddSysJobDto dto) throws SchedulerException, TaskException {
        SysJob sysJob = BeanCopyUtils.copyBean(dto, SysJob.class);
        sysJob.setStatus(ScheduleConstants.Status.PAUSE.getValue());
        if (sysJobMapper.insert(sysJob) > 0) {
            ScheduleUtils.createScheduleJob(scheduler, sysJob);
            return sysJob.getId();
        }
        return null;
    }

    @Transactional
    @Override
    public void update(Long id, AddSysJobDto dto) throws SchedulerException, TaskException {
        SysJob sysJob = sysJobMapper.selectById(id);
        String group = sysJob.getJobGroup();
        BeanCopyUtils.copyNonNullProperties(dto, sysJob);
        if (sysJobMapper.updateById(sysJob) > 0) {
            updateSchedulerJob(sysJob, group);
        }
    }

    @Override
    public boolean checkCronExpressionIsValid(String cronExpression) {
        return CronUtils.isValid(cronExpression);
    }

    /**
     * 更新任务
     *
     * @param job      任务对象
     * @param jobGroup 任务组名
     */
    public void updateSchedulerJob(SysJob job, String jobGroup) throws SchedulerException, TaskException {
        Long jobId = job.getId();
        // 判断是否存在
        JobKey jobKey = ScheduleUtils.getJobKey(jobId, jobGroup);
        if (scheduler.checkExists(jobKey)) {
            // 防止创建时存在数据问题 先移除，然后在执行创建操作
            scheduler.deleteJob(jobKey);
        }
        ScheduleUtils.createScheduleJob(scheduler, job);
    }

    private List<SysJob> listAll() {
        return sysJobMapper.selectList(null);
    }
}
